﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using XPressify.Data;
using XPressify.Data.Helpers;
using XPressify.Helpers;
using XPressify.Models;
using NotificationType = XPressify.Models.NotificationType;

namespace XPressify.Controllers
{
    public class PostController : SubjectController
    {
        [Authorize]
        public ActionResult PostLike(Guid id)
        {
            var post = Repository.AsQueryable<Post>().SingleOrDefault(pst => pst.Id == id);
            var user = CurrentUser;

            bool alreadyLiked;

            //if is post with global comments than we look for all likes that are connected with post.BaseObject // global likes
            if(post.CommentType == CommentType.PostWithGlobalComments)
            {

                alreadyLiked =
                    Repository.AsQueryable<PostLike>().Any(
                        pst =>
                        pst.BaseSubject.Id == user.Id && pst.Post.BaseObject.Id == post.BaseObject.Id &&
                        pst.Post.CommentType == CommentType.PostWithGlobalComments);
            }
            else
            {
                alreadyLiked =
                    Repository.AsQueryable<PostLike>().Any(
                        pst => pst.BaseSubject.Id == user.Id && pst.Post.Id == post.Id);
            }
            
            if(alreadyLiked == false)
            {
                var postLike = new PostLike()
                {
                    BaseSubject = user,
                    Created = DateTime.Now,
                    Id = Guid.NewGuid(),
                    Note = 0,
                    Post = post,
                    Type = BaseMessageType.PostLike
                };
                Repository.AddObject(postLike);



                
                var creator = post.BaseSubject;
                if(post.BaseSubject.Type != SubjectType.User)
                {
                    creator = post.Creator;
                }

                //if user is not createor then we create a notification
                if(user.Id != creator.Id)
                {
                    //create UserLikesYourPost notification
                    var newNotification = new Notification()
                    {
                        Active = true,
                        BaseSubject = creator,
                        Created = DateTime.Now,
                        Id = Guid.NewGuid(),
                        Type = BaseMessageType.Notification
                    };

                    var subjectType = "";

                    if (post.BaseSubject.Type == SubjectType.User)
                    {
                        subjectType = "user";
                    }
                    else if (post.BaseSubject.Type == SubjectType.Event)
                    {
                        subjectType = "event";
                    }
                    else if (post.BaseSubject.Type == SubjectType.Group)
                    {
                        subjectType = "group";
                    }
                    else if (post.BaseSubject.Type == SubjectType.Page)
                    {
                        subjectType = "page";
                    }


                    var viewData2 = new
                    {
                        UserId = user.Id,
                        UserFullname = user.FullName,
                        PostOwnerId = post.BaseSubject.Id,
                        PostId = post.Id,
                        BaseSubjectType = subjectType
                    };

                    var viewDictionary2 = viewData2.ToViewDataDictionary();
                    newNotification.Content = ControlToString("~/Views/Notification/UserLikesYourPost.cshtml", viewDictionary2);
                    Repository.AddObject(newNotification);
                    
                }
                Repository.SaveChanges();
            }
            else // if there is post like then we unlike them so we delete the postLike post
            {
                if (post.CommentType == CommentType.PostWithGlobalComments)
                {
                    var postLike =
                        Repository.AsQueryable<PostLike>().SingleOrDefault(
                            p =>
                            p.BaseSubject.Id == user.Id && p.Post.BaseObject.Id == post.BaseObject.Id &&
                            p.Post.CommentType == CommentType.PostWithGlobalComments);
//                            p.Post.PostType != PostType.Comment);
                    if (postLike != null)
                    {
                        postLike.BaseSubject = null;
                        Repository.DeleteObject(postLike);
                        Repository.SaveChanges();
                    }
                }
                else
                {
                    var postLike =
                        Repository.AsQueryable<PostLike>().SingleOrDefault(
                            p => p.BaseSubject.Id == user.Id && p.Post.Id == post.Id);
                    if (postLike != null)
                    {
                        postLike.BaseSubject = null;
                        Repository.DeleteObject(postLike);
                        Repository.SaveChanges();
                    }
                }
                
            }

            return PartialView("EmptyView");

        }

        [Authorize]
        public ActionResult PostLikeList(Guid id)
        {
            var post = Repository.AsQueryable<Post>().SingleOrDefault(pst => pst.Id == id);
            IEnumerable<PostLike> allPostLikes;


            //if is post with global comments than we look for all likes that are connected with post.BaseObject // global likes
            if(post.CommentType == CommentType.PostWithGlobalComments)
            {
                //find all postLikes from the same BaseObject
                allPostLikes =
                    Repository.AsQueryable<PostLike>().Where(
                        like => like.Post.BaseObject.Id == post.BaseObject.Id &&
                                like.Post.CommentType == CommentType.PostWithGlobalComments);
//                        like.Post.PostType != PostType.Comment);
            }
            else
            {
                //all postLikes from that post only
                allPostLikes =
                    Repository.AsQueryable<PostLike>().Where(like => like.Post.Id == post.Id);
            }
            
            var listPeople = new List<People>();
            foreach (var allPostLike in allPostLikes)
            {
                var user = (User)allPostLike.BaseSubject;
                var profilePicturePath = Repository.GetProfilePicturePath(user.Id);
                var people = new People()
                                 {
                                     FullName = user.FullName,
                                     Id = user.Id,
                                     ProfilePicturePath = profilePicturePath
                                 };
                listPeople.Add(people);
            }
            var model = new PostLikeListModel
                            {
                                Users = listPeople.ToArray(),
                                Id = post.BaseSubject.Id
                            };

            return PartialView(model);
        }

        [Authorize]
        [HttpPost]
        public ActionResult PostComment(PostCommentModel model)
        {
            var user = CurrentUser;
            var post = Repository.AsQueryable<Post>().SingleOrDefault(pst => pst.Id == model.PostId);
            var newPostComment = new Post
            {
                BaseObject = post.BaseObject,
                BaseSubject = user,
                Created = DateTime.Now,
                Id = Guid.NewGuid(),
                Title = "Post comment",
                Description = "Post comment",
                PostType = PostType.Comment,
                Type = BaseMessageType.Comment,
                Parent = post,
                CommentType = CommentType.PostWithItsComments
            };


            string profilePicturePath = Repository.GetProfilePicturePath(user.Id);

            var picturePath = profilePicturePath;
            var subjectId = user.Id;
            var subjectFullname = user.FullName;
            var isAdminPage = "false";

            //check if its page and its admin if so then post will show that "page" commenter something not the actual user
            if (post.BaseSubject.Type == SubjectType.Page)
            {
                var page = Repository.AsQueryable<Page>().SingleOrDefault(pg => pg.Id == post.BaseSubject.Id);
                var isAdmin =
                    Repository.AsQueryable<Relationship>().Any(
                        rel =>
                        rel.BaseSubject.Id == user.Id && rel.RelationType == RelationshipType.AdministratorOf);
                if (isAdmin)
                {
                    subjectId = page.Id;
                    subjectFullname = page.FullName;
                    isAdminPage = "true";

                    //default cover picture path
                    var album = Repository.AsQueryable<Album>().SingleOrDefault(al => al.BaseSubject.Id == page.Id);
                    picturePath = "/Media/" + post.BaseSubject.Id + "/" + album.Id + "/CoverPicture/CoverPicture";
                }
            }



            var viewData = new
            {
                Id = subjectId,
                ProfilePicturePath = picturePath,
                Type = "group",
                Fullname = subjectFullname,
                Content = model.ContentText,
                PostId = newPostComment.Id,
                Created = newPostComment.Created,
                IsAdminPage = isAdminPage
            };

            var viewDictionary = viewData.ToViewDataDictionary();
            newPostComment.Content = ControlToString("~/Views/Post/Comment.cshtml", viewDictionary);
            Repository.AddObject(newPostComment);
            Repository.SaveChanges();

            //if post that user commented not belongs to the current user then we create extra post that will show on user wall that currentUser commented on other post
            if(post.BaseSubject.Id != user.Id && post.BaseSubject.Type == SubjectType.User)
            {
                //now create a post CommentedOnFriendsPost
                var newPostComment1 = new Post
                {
                    BaseObject = post.BaseObject,
                    BaseSubject = user,
                    Created = DateTime.Now,
                    Id = Guid.NewGuid(),
                    Title = "Post comment - post on your wall",
                    Description = "Post comment - post on your wall", 
                    PostType = PostType.Post,
                    Type = BaseMessageType.Post,
                    CommentType = CommentType.PostWithoutComments
                };

                var viewData1 = new
                {
                    YourId = user.Id,
                    ProfilePicturePath = profilePicturePath,
                    YourFullname = user.FullName,
                    FriendsId = post.BaseSubject.Id,
                    FriendsPostId = post.Id,
                    FriendFullName = post.BaseSubject.FullName
                };

                viewDictionary = viewData1.ToViewDataDictionary();
                newPostComment1.Content = ControlToString("~/Views/Post/CommentedOnFriendsPost.cshtml", viewDictionary);
                Repository.AddObject(newPostComment1);
                Repository.SaveChanges();

            }

            
            
            //create a notifications
            var allParticipants = Repository.GetAllComments(post).Select(p => p.BaseSubject).OfType<User>().Distinct(new UserComparer());
            var allParticipantsList = allParticipants.ToList();
            
            //creator of the post if BaseSubject not User then post has Creator
            var creator = post.BaseSubject.Type == SubjectType.User ? (User) post.BaseSubject : (User) post.Creator;
            
            //if creator is not on the list we put it but if its current user creator then we dont make notification for current user
            if (!allParticipantsList.Any(par => par.Id == creator.Id) && creator.Id != user.Id)
            {
                allParticipantsList.Add(creator);
            }
            var dateNow = DateTime.Now;

            var subjectType = "";
            
            if(post.BaseSubject.Type == SubjectType.User)
            {
                subjectType = "user";
            }
            else if (post.BaseSubject.Type == SubjectType.Event)
            {
                subjectType = "event";
            }
            else if (post.BaseSubject.Type == SubjectType.Group)
            {
                subjectType = "group";
            }
            else if (post.BaseSubject.Type == SubjectType.Page)
            {
                subjectType = "page";
            }
            //we create notification for every user who commented on this post and creator of the post)
            foreach (var participant in allParticipantsList)
            {
                if(participant.Id == user.Id)
                    continue;
                // UserCommentedOnPost notification
                var newNotification = new Notification()
                                      {
                                          Active = true,
                                          BaseSubject = participant,
                                          Created = dateNow,
                                          Id = Guid.NewGuid(),
                                          Type = BaseMessageType.Notification
                                      };
                var viewData2 = new
                {
                    UserId = user.Id,
                    UserFullname = user.FullName,
                    PostOwnerId = post.BaseSubject.Id,
                    PostId = post.Id,
                    BaseSubjectType = subjectType
                };

                var viewDictionary2 = viewData2.ToViewDataDictionary();
                newNotification.Content = ControlToString("~/Views/Notification/UserCommentedOnPost.cshtml", viewDictionary2);
                Repository.AddObject(newNotification);
                Repository.SaveChanges();

            }

            return PartialView("CommentPost", new CommentPostModel() { Post = newPostComment });
            
        } 

        
        [Authorize]
        [HttpPost]
        public ActionResult PostDelete(PostDeleteModel model)
        {
            var post = Repository.AsQueryable<Post>().SingleOrDefault(pst => pst.Id == model.PostId);
            var profileId = post.BaseSubject.Id;


            // if its link, not-e or status but not postWithoutComment then we delete object also
            var isNoteStatusLink = ((post.BaseObject.Type == ObjectType.Status ||
                                     post.BaseObject.Type == ObjectType.Link ||
                                     post.BaseObject.Type == ObjectType.Note) &&
                                    post.CommentType == CommentType.PostWithItsComments &&
                                    post.PostType != PostType.Comment);

            //if its previous love relationship by default which is status.Blocked then we check if this is the last post connected to that relationship if so then we delete relationship also
            //we leave previous relationship for post connected to it but if its last post then we delete it
            var isPreviousLoveRelationship = post.BaseObject.Type == ObjectType.Relationship &&
                                             IsLoveRelationship((Relationship) post.BaseObject) &&
                                             ((Relationship) post.BaseObject).Status == RelationshipStatus.Blocked;
            var loveRelationship = new Relationship();
            var isLastPostForPreviousLoveRelatinship = false;
            if(isPreviousLoveRelationship)
            {
                loveRelationship = (Relationship) post.BaseObject;
                //we look if there is another post that is connected to love relationship
                isLastPostForPreviousLoveRelatinship =
                    !Repository.AsQueryable<Post>().Any(
                        p => p.BaseObject.Id == loveRelationship.Id && p.PostType == PostType.Post && p.Id != post.Id);
            }

            
            //if its not-e, link or status we delete object and automaticly delete all comments and postLikes connected to that object
            if (isNoteStatusLink)
            {
                if (post.BaseObject.Type == ObjectType.Status)
                {
                    var status = (Status) post.BaseObject;
                    Repository.DeleteObject(status);
                    Repository.SaveChanges();
                }

                else if (post.BaseObject.Type == ObjectType.Link)
                {
                    var link = (Link)post.BaseObject;
                    Repository.DeleteObject(link);
                    Repository.SaveChanges();
                }

                else if (post.BaseObject.Type == ObjectType.Note)
                {
                    var note = (Note)post.BaseObject;
                    Repository.DeleteObject(note);
                    Repository.SaveChanges();
                }
            }

            //take all comments and postLikes that are connected to this post and try to connect to other post that has same baseObject
            else if (post.CommentType == CommentType.PostWithGlobalComments)
            {
                var postLikes = Repository.AsQueryable<PostLike>().Where(pst => pst.Post.Id == post.Id);
                var postComments =
                    Repository.AsQueryable<Post>().Where(
                        pst => pst.PostType == PostType.Comment && pst.Parent.Id == post.Id);
                var otherPosts =
                    Repository.AsQueryable<Post>().Where(
                        pst =>
                        pst.BaseObject.Id == post.BaseObject.Id && pst.Id != post.Id &&
                        pst.CommentType == CommentType.PostWithGlobalComments);
                if (otherPosts.Count() != 0)
                {
                    var other = otherPosts.First(); // get the first random element

                    // we put postLikes to other post that is connected with the same BaseObject so we preserve all PostLikes from this object
                    foreach (var postLike in postLikes)
                    {
                        postLike.Post = other;
                    }

                    // we put postComment to other post that is connected with the same BaseObject so we preserve all PostComments from this object
                    foreach (var postComment in postComments)
                    {
                        postComment.Parent = other;
                    }
                }
                Repository.DeleteObject(post);
                Repository.SaveChanges();
            }
            //it will delete only its comments and postLikes
            else if (post.CommentType != CommentType.PostWithGlobalComments)
            {
                Repository.DeleteObject(post);
                Repository.SaveChanges();
            }

            // if its love relationship and this was last post then we delete the object also
            if (isPreviousLoveRelationship && isLastPostForPreviousLoveRelatinship)
            {
                Repository.DeleteObject(loveRelationship);
                Repository.SaveChanges();
            }

            return PartialView("EmptyView");
        }


        [Authorize]
        [HttpPost]
        public ActionResult PostCommentDelete(PostDeleteModel model)
        {
            var post = Repository.AsQueryable<Post>().SingleOrDefault(pst => pst.Id == model.PostId);
            
            Repository.DeleteObject(post);
            Repository.SaveChanges();
            return PartialView("EmptyView");
        }

        #region Relationship functions
        public override ActionResult ConfirmRelationshipNotification(Relationship relationship)
        {
            throw new NotImplementedException();
        }

        public override ActionResult PostponeRelationshipNotification(Relationship relationship)
        {
            throw new NotImplementedException();
        }

        public override ActionResult PostponeFailedRelationshipNotification(Relationship relationship)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region Extra functions
        public bool IsLoveRelationship(Relationship rel)
        {
            if ((rel.RelationType == RelationshipType.EngagedWith ||
                rel.RelationType == RelationshipType.InRelationshipWith ||
                rel.RelationType == RelationshipType.IsDivorced ||
                rel.RelationType == RelationshipType.IsSingle ||
                rel.RelationType == RelationshipType.IsWidowed ||
                rel.RelationType == RelationshipType.ItsComplicatedWith ||
                rel.RelationType == RelationshipType.MarriedWith ||
                rel.RelationType == RelationshipType.OpenRelationshipWith))
            {
                return true;
            }
            return false;
        }
        #endregion

        #region Extra class

        public class UserComparer : IEqualityComparer<User>
        {

            bool  IEqualityComparer<User>.Equals(User x, User y)
            {
                if (x == null || y == null)
                    return false;
                if (x.Id == y.Id)
                    return true;
                return false;
            }

            int  IEqualityComparer<User>.GetHashCode(User obj)
            {
                return obj.Id.GetHashCode();
            }

        }

        #endregion
    }
}
